﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Baml2006;
using System.Windows.Documents;
using System.Windows.Media.Imaging;
using System.Xml;
using System.Xml.XPath;
using Kokomo.Environment;
using Kokomo.Environment.Commanding;
using Kokomo.Environment.Documents;
using Kokomo.Environment.Services;
using Kokomo.Mirror.CliMetadata;
using Kokomo.Mirror.PeFormat;
using Kokomo.PeInspector.Extensibility;
using Kokomo.PeInspector.Reflection.Disassembly;
using Kokomo.PeInspector.Settings;
using System.Text;
using System.Windows.Input;

namespace Kokomo.PeInspector.Reflection
{
	[ViewType(typeof(AssemblyAnalyzerView))]
	class AssemblyAnalyzerDocumentViewModel : NavigationDocumentViewModel, IMetadataHost
	{
		public AssemblyAnalyzerDocumentViewModel(
			IAssemblyListHost assemblyListHost
			)
			: base()
		{
			if (assemblyListHost == null)
				assemblyListHost = this as IAssemblyListHost;
			if (assemblyListHost == null) throw new ArgumentNullException("assemblyListHost");

			this.AssemblyListHost = assemblyListHost;
			this.HelpMenuItemViewModels = AssemblyAnalyzerEditor.HelpMenuItemViewModels;

			this.ViewModuleDetailsCommand = new ParameterizedCommand(this.ViewModuleDetails);
			this.ExportAssemblySourceCodeCommand = new ParameterizedCommand(this.ExportAssemblySourceCode);
		}

		public ICommand ViewModuleDetailsCommand { get; private set; }
		public ICommand ExportAssemblySourceCodeCommand { get; private set; }

		public IAssemblyListHost AssemblyListHost { get; private set; }

		public MethodDecompilation GetDisassemblyResult(MethodBase method)
		{
			//return this.MethodDecompilerResultCache.GetItem(method);
			return this.CreateMethodDisassemblyResult(method);
		}
		private MethodDecompilation CreateMethodDisassemblyResult(MethodBase method)
		{
			MethodDecompilation result = MsilDecompiler.Decompile(method, MsilDecompilationFlags.Full);
			return result;
		}

		private IEnumerable<TreeNodeViewModel> GetTypeNodeItemsSource(Type type)
		{
			IEnumerable<MemberInfo> members = type.GetMembers(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly).OrderBy(m => m.Name);
			var q = members.Select(this.CreateMemberNode).ToArray();

			return q;
		}
		public TreeNodeViewModel CreateMemberNode(MemberInfo member)
		{
			MemberViewInfo viewInfo = MemberViewInfo.GetMemberViewInfo(member);

			Func<ProgressInfo, IEnumerable<TreeNodeViewModel>> itemsSourceDelegate = null;
			switch (member.MemberType)
			{
				case MemberTypes.TypeInfo:
				case MemberTypes.NestedType:
					Type type = (Type)member;
					itemsSourceDelegate = (progress) => GetTypeNodeItemsSource(type);
					break;
			}

			TreeNodeViewModel memberNode = new TreeNodeViewModel(AssemblyAnalyzerEditor.MetadataIconProvider.GetImage(viewInfo.IconKey), viewInfo.Heading, member) { ItemsSourceDelegate = itemsSourceDelegate };
			memberNode.ContextMenuItems = new MenuItemViewModel[] {
				new MenuCommandItemViewModel(new CommandViewModel(this.AssemblyListHost.AddFavoriteCommand, UiString.FromString(Messages.AddFavoriteCommandText)) {Argument=member})
			};

			return memberNode;
		}
		public IEnumerable<MsilDependency> GetDependenciesOf(MethodBase method)
		{
			if (method == null) throw new ArgumentNullException("method");

			return MsilDecompiler.GetDependenciesOf(method);
		}
		public void BuildSourceDocument(MemberInfo member, ICodeWriter writer)
		{
			if (member == null) throw new ArgumentNullException("member");
			if (writer == null) throw new ArgumentNullException("writer");

			CSharpDocumentBuilder builder = new CSharpDocumentBuilder(
				writer,
				member,
				this.GetDisassemblyResult,
				AssemblyAnalyzerEditor.GetSourceDocumentNamespaceImports(member.Module.Assembly)
				);
			builder.WriteMemberDeclaration(member);
		}
		public void BuildSourceDocument(Assembly assembly, ICodeWriter writer)
		{
			if (assembly == null) throw new ArgumentNullException("assembly");
			if (writer == null) throw new ArgumentNullException("writer");

			CSharpDocumentBuilder builder = new CSharpDocumentBuilder(writer, null, this.GetDisassemblyResult, AssemblyAnalyzerEditor.AssemblyInfoNamespaceImports);
			builder.WriteDeclarationAttributes(assembly);
		}
		public void BuildMsilDocument(MemberInfo member, ICodeWriter writer)
		{
			if (member == null) throw new ArgumentNullException("member");
			if (writer == null) throw new ArgumentNullException("writer");

			MsilDocumentBuilder builder = new MsilDocumentBuilder(
				writer,
				member,
				this.GetDisassemblyResult
				);
			builder.WriteMemberDeclaration(member);
		}

		protected DetailViewModel GetDetailViewModelForMember(
			MemberInfo member,
			NavigationContext navigationContext
			)
		{
			if (member == null) throw new ArgumentNullException("member");

			MemberViewInfo viewInfo = MemberViewInfo.GetMemberViewInfo(member);
			TabbedDetailViewModel detailViewModel = new TabbedDetailViewModel(
				navigationContext,
				member,
				viewInfo.Title
				);
			detailViewModel.Summary.Commands.Add(new CommandViewModel(
				this.AssemblyListHost.AddFavoriteCommand,
				UiString.FromString(Messages.AddToFavoritesText)
				) { Argument = member });

			detailViewModel.Summary.Details.Add(new SummaryDetailItem(
				UiString.FromString(Messages.MetadataTokenLabel),
				UiString.FromString(string.Format("0x{0:X8}", member.MetadataToken))
				));
			detailViewModel.Summary.Details.Add(new SummaryDetailLinkItem(
				UiString.FromString(Messages.AssemblyLabel),
				UiString.FromString(member.Module.Assembly.FullName),
				navigationContext.NavigateToCommand,
				member.Module.Assembly
				));

			Type reflectedType = member.DeclaringType;
			if (reflectedType != null)
			{
				detailViewModel.Summary.Details.Add(new SummaryDetailLinkItem(
					UiString.FromString(Messages.DeclaringTypeLabel),
					UiString.FromString(reflectedType.FullName),
					navigationContext.NavigateToCommand,
					reflectedType
					));
			}

			SourceDocumentViewModel sourceDocumentViewModel = new SourceDocumentViewModel(
				writer => this.BuildSourceDocument(member, writer),
				navigationContext,
				AssemblyAnalyzerEditor.CSharpFileType,
				member.Name
				);
			SourceDocumentViewModel msilDocumentViewModel = new SourceDocumentViewModel(
				writer => this.BuildMsilDocument(member, writer),
				navigationContext,
				AssemblyAnalyzerEditor.MsilFileType,
				member.Name
				);
			IImageProvider metadataIconProvider = AssemblyAnalyzerEditor.MetadataIconProvider;
			IImageProvider standardIconProvider = EnvironmentServices.DemandService<IImageProvider>();
			DependencyGraphHelper graphHelper = new DependencyGraphHelper(this.AssemblyListHost);

			switch (member.MemberType)
			{
				case MemberTypes.Constructor:
				case MemberTypes.Method:
					{
						MethodBase methodBase = (MethodBase)member;
						detailViewModel.Tabs.Add(new TabPageViewModel(UiString.FromString(Messages.SourceTitle), sourceDocumentViewModel, metadataIconProvider.GetImage(ImageKeys.CSharpCode)));
						detailViewModel.Tabs.Add(new TabPageViewModel(UiString.FromString(Messages.MsilTitle), msilDocumentViewModel, metadataIconProvider.GetImage(ImageKeys.MsilCode)));

						IExtendedMethodInfo extendedMethod = methodBase as IExtendedMethodInfo;
						if (extendedMethod != null)
						{
							if (extendedMethod.Rva > 0)
							{
								detailViewModel.Summary.Details.Add(new SummaryDetailItem(
									UiString.FromString(Messages.RvaLabel),
									UiString.FromString(string.Format("0x{0:X16}", extendedMethod.Rva))
									));
							}
							if (methodBase.GetMethodImplementationFlags().HasFlag(MethodImplAttributes.InternalCall))
							{
								SourceDocumentViewModel disassemblyViewModel = new SourceDocumentViewModel(
									(writer) => DisassemblyDocumentBuilder.BuildDisassemblyDocument(extendedMethod.GetNativeImplementation(), writer),
									navigationContext,
									AssemblyAnalyzerEditor.AssemblyListingFileType,
									methodBase.Name
									);

								detailViewModel.Tabs.Add(new TabPageViewModel(
									UiString.FromString(Messages.DisassemblyTitle),
									disassemblyViewModel
									));
							}
						}

						detailViewModel.Tabs.Add(new TabPageViewModel(UiString.FromString(Messages.CodeMetricsTitle), new CodeMetricsViewModel(methodBase)));

						DependencyGraphsViewModel graphs = new DependencyGraphsViewModel(navigationContext.NavigateToCommand);

						if (
							(methodBase.DeclaringType != null)
							&& (methodBase.DeclaringType.IsInterface)
							)
						{
							graphs.AddGraph(new DependencyGraphViewModel(this, new DependencyGraph(
								methodBase,
								graphHelper.GetImplementations),
								UiString.FromString(Messages.ImplementedByTitle)
								));
						}
						else
						{
							graphs.AddGraph(new DependencyGraphViewModel(this, new DependencyGraph(
								methodBase,
								graphHelper.GetCallDependencies),
								UiString.FromString(Messages.CallGraphTitle)
								));
						}
						graphs.AddGraph(new DependencyGraphViewModel(this, new DependencyGraph(
							methodBase,
							graphHelper.GetCallerDependencies),
							UiString.FromString(Messages.CallerGraphTitle)
							));

						detailViewModel.Tabs.Add(new TabPageViewModel(UiString.FromString(Messages.GraphsTitle), graphs));

						if (member.MemberType == MemberTypes.Method)
						{
							MethodInfo method = (MethodInfo)member;

							if (method.ReturnType != null)
							{
								MemberViewInfo returnTypeViewInfo = MemberViewInfo.GetMemberViewInfo(method.ReturnType);
								detailViewModel.Summary.Details.Add(new SummaryDetailLinkItem(
									UiString.FromString(Messages.ReturnTypeLabel),
									UiString.FromString(returnTypeViewInfo.LongHeading),
									navigationContext.NavigateToCommand,
									method.ReturnType
									));
							}

							MethodInfo baseMethod = method.GetBaseDefinition();
							if ((baseMethod != null) && (baseMethod != method))
							{
								MemberViewInfo baseMethodViewInfo = MemberViewInfo.GetMemberViewInfo(baseMethod);

								detailViewModel.Summary.Details.Add(new SummaryDetailLinkItem(
									UiString.FromString(Messages.BaseMethodLabel),
									UiString.FromString(viewInfo.LongHeading),
									navigationContext.NavigateToCommand,
									baseMethod
									));
							}
						}
					}
					break;
				case MemberTypes.TypeInfo:
				case MemberTypes.NestedType:
					{
						Type type = (Type)member;
						if (type.Namespace != null)
						{
							NamespaceScope namespaceScope = this.AssemblyListHost.GetAssemblyIndex(type.Assembly).GetNamespaceScope(type.Namespace);

							if (namespaceScope != null)
							{
								detailViewModel.Summary.Details.Add(new SummaryDetailLinkItem(
									UiString.FromString(Messages.NamespaceLabel),
									UiString.FromString(type.Namespace),
									navigationContext.NavigateToCommand,
									namespaceScope
									));
							}
							else
							{
								detailViewModel.Summary.Details.Add(new SummaryDetailItem(
									UiString.FromString(Messages.NamespaceLabel),
									UiString.FromString(type.Namespace)
									));
							}
						}
						if (type.BaseType != null)
						{
							MemberViewInfo baseTypeViewInfo = MemberViewInfo.GetMemberViewInfo(type.BaseType);
							detailViewModel.Summary.Details.Add(new SummaryDetailLinkItem(
								UiString.FromString(Messages.BaseTypeLabel),
								UiString.FromString(baseTypeViewInfo.LongHeading),
								navigationContext.NavigateToCommand,
								type.BaseType
								));
						}

						detailViewModel.Tabs.Add(new TabPageViewModel(UiString.FromString(Messages.MembersTitle), new TypeMembersViewModel(type, this, navigationContext.NavigateToCommand)));
						detailViewModel.Tabs.Add(new TabPageViewModel(UiString.FromString(Messages.SourceTitle), sourceDocumentViewModel, metadataIconProvider.GetImage(ImageKeys.CSharpCode)));
						detailViewModel.Tabs.Add(new TabPageViewModel(UiString.FromString(Messages.MsilTitle), msilDocumentViewModel, metadataIconProvider.GetImage(ImageKeys.MsilCode)));

						Stream bamlStream = this.GetTypeBamlStream(type);
						if (bamlStream != null)
						{
							SourceDocumentViewModel xamlViewModel = CreateBamlViewModel(
								bamlStream,
								type.Name,
								type.Assembly,
								navigationContext
								);
							detailViewModel.Tabs.Add(new TabPageViewModel(UiString.FromString(Messages.XamlTitle), xamlViewModel, metadataIconProvider.GetImage(ImageKeys.Xaml)));
						}

						DependencyGraphsViewModel graphs = new DependencyGraphsViewModel(navigationContext.NavigateToCommand);

						if (type.IsInterface)
						{
							graphs.AddGraph(new DependencyGraphViewModel(
								this,
								new DependencyGraph(type, graphHelper.GetImplementingDependencies),
								UiString.FromString(Messages.ImplementedByTitle)
								));
						}
						else
						{
							if (type.IsAttribute())
							{
								graphs.AddGraph(new DependencyGraphViewModel(
									this,
									new DependencyGraph(type, graphHelper.GetAttributeDependencies),
									UiString.FromString(Messages.AttributeAppliedToTitle)
									));
							}
							else if (type.IsException())
							{
								graphs.AddGraph(new DependencyGraphViewModel(
									this,
									new DependencyGraph(type, graphHelper.GetAttributeDependencies),
									UiString.FromString(Messages.ThrownByTitle)
									));
							}
							graphs.AddGraph(new DependencyGraphViewModel(
									this,
									new DependencyGraph(type, graphHelper.GetDerivedDependencies),
									UiString.FromString(Messages.ExtendedByTitle)
									));
							graphs.AddGraph(new DependencyGraphViewModel(
								this,
								new DependencyGraph(type, graphHelper.GetInstantiationDependencies),
								UiString.FromString(Messages.InstantiatedByTitle)
								));
						}

						graphs.AddGraph(new DependencyGraphViewModel(
								this,
								new DependencyGraph(type, graphHelper.GetExtensionDependencies),
								UiString.FromString(Messages.ExtensionMethodsTitle)
								));

						graphs.AddGraph(new DependencyGraphViewModel(
							this,
							new DependencyGraph(type, graphHelper.GetAcceptedByDependencies),
							UiString.FromString(Messages.AcceptedByTitle)
							));
						graphs.AddGraph(new DependencyGraphViewModel(
							this,
							new DependencyGraph(type, graphHelper.GetReturnedByDependencies),
							UiString.FromString(Messages.ReturnedByTitle)
							));
						graphs.AddGraph(new DependencyGraphViewModel(
							this,
							new DependencyGraph(type, graphHelper.GetReferenceDependencies),
							UiString.FromString(Messages.ReferencedByTitle)
							));

						detailViewModel.Tabs.Add(new TabPageViewModel(UiString.FromString(Messages.GraphsTitle), graphs));
					}
					break;
				case MemberTypes.Field:
					{
						FieldInfo field = (FieldInfo)member;
						if (field.FieldType != null)
						{
							MemberViewInfo returnTypeViewInfo = MemberViewInfo.GetMemberViewInfo(field.FieldType);
							detailViewModel.Summary.Details.Add(new SummaryDetailLinkItem(
								UiString.FromString(Messages.FieldTypeLabel),
								UiString.FromString(returnTypeViewInfo.LongHeading),
								navigationContext.NavigateToCommand,
								field.FieldType
								));
						}

						detailViewModel.Tabs.Add(new TabPageViewModel(UiString.FromString(Messages.SourceTitle), sourceDocumentViewModel, metadataIconProvider.GetImage(ImageKeys.CSharpCode)));
						detailViewModel.Tabs.Add(new TabPageViewModel(UiString.FromString(Messages.MsilTitle), msilDocumentViewModel, metadataIconProvider.GetImage(ImageKeys.MsilCode)));

						DependencyGraphsViewModel graphs = new DependencyGraphsViewModel(navigationContext.NavigateToCommand);

						graphs.AddGraph(new DependencyGraphViewModel(
							this,
							new DependencyGraph(member, graphHelper.GetSetterDependencies),
							UiString.FromString(Messages.AssignedByHeading)
							));
						graphs.AddGraph(new DependencyGraphViewModel(
							this,
							new DependencyGraph(member, graphHelper.GetUsedDependencies),
							UiString.FromString(Messages.UsedByHeading)
							));

						detailViewModel.Tabs.Add(new TabPageViewModel(UiString.FromString(Messages.GraphsTitle), graphs));
					}
					break;
				case MemberTypes.Event:
					{
						EventInfo eventInfo = (EventInfo)member;
						if (eventInfo.EventHandlerType != null)
						{
							MemberViewInfo returnTypeViewInfo = MemberViewInfo.GetMemberViewInfo(eventInfo.EventHandlerType);
							detailViewModel.Summary.Details.Add(new SummaryDetailLinkItem(
								UiString.FromString(Messages.EventHandlerTypeLabel),
								UiString.FromString(returnTypeViewInfo.LongHeading),
								navigationContext.NavigateToCommand,
								eventInfo.EventHandlerType
								));
						}

						detailViewModel.Tabs.Add(new TabPageViewModel(UiString.FromString(Messages.SourceTitle), sourceDocumentViewModel, metadataIconProvider.GetImage(ImageKeys.CSharpCode)));
						detailViewModel.Tabs.Add(new TabPageViewModel(UiString.FromString(Messages.MsilTitle), msilDocumentViewModel, metadataIconProvider.GetImage(ImageKeys.MsilCode)));
					}
					break;
				case MemberTypes.Property:
					{
						PropertyInfo property = (PropertyInfo)member;
						if (property.PropertyType != null)
						{
							MemberViewInfo returnTypeViewInfo = MemberViewInfo.GetMemberViewInfo(property.PropertyType);
							detailViewModel.Summary.Details.Add(new SummaryDetailLinkItem(
								UiString.FromString(Messages.PropertyTypeLabel),
								UiString.FromString(returnTypeViewInfo.LongHeading),
								navigationContext.NavigateToCommand,
								property.PropertyType
								));
						}

						detailViewModel.Tabs.Add(new TabPageViewModel(UiString.FromString(Messages.SourceTitle), sourceDocumentViewModel, metadataIconProvider.GetImage(ImageKeys.CSharpCode)));
						detailViewModel.Tabs.Add(new TabPageViewModel(UiString.FromString(Messages.MsilTitle), msilDocumentViewModel, metadataIconProvider.GetImage(ImageKeys.MsilCode)));
					}
					break;
				default:
					break;
			}

			XmlDocument documentation = this.AssemblyListHost.GetAssemblyIndex(member.Module.Assembly).GetDocumentationFor(member);
			if (documentation != null)
			{
				FlowDocumentViewModel documentationViewModel = new FlowDocumentViewModel(
					this.GetXmlDocBuilder(member, documentation, navigationContext),
					navigationContext,
					member.Name
					);
				detailViewModel.Tabs.Add(new TabPageViewModel(
					UiString.FromString(Messages.DocumentationTitle),
					documentationViewModel,
					standardIconProvider.GetImage(StandardImageKeys.Help)
					));
			}

			Type declaringType =
				member as Type
				?? member.DeclaringType;
			if (
				(declaringType != null)
				&& (declaringType.Namespace != null)
				&& declaringType.Namespace.StartsWith("System")
				&& (
					declaringType.IsVisible
					)
				)
			{
				detailViewModel.Summary.Commands.Add(new CommandViewModel(
					new ParameterizedCommand(this.ViewOnlineDocumentation),
					UiString.FromString(Messages.ViewMsdnDocumentationText)
					) { Argument = member });
			}

			return detailViewModel;
		}

		private void ViewOnlineDocumentation(object arg)
		{
			MemberInfo member = arg as MemberInfo;
			if (member == null) return;

			Type type = member as Type;
			bool isType = (type != null);
			if (!isType) type = member.DeclaringType;

			string msdnKey;
			if (type.DeclaringType != null)
			{
				msdnKey = type.DeclaringType.FullName + "." + type.Name;
			}
			else
			{
				msdnKey = type.FullName;
			}

			if (!isType)
			{
				msdnKey += "." + member.Name;
			}

			string msdnUrl = "http://msdn.microsoft.com/en-us/library/" + msdnKey;
			Process.Start(msdnUrl);
		}

		private SourceDocumentViewModel CreateBamlViewModel(
			Stream bamlStream,
			string title,
			Assembly assembly,
			NavigationContext navigationContext
			)
		{
			SourceDocumentViewModel xamlViewModel = new SourceDocumentViewModel(
				this.GetXamlBuilder(bamlStream, assembly),
				navigationContext,
				AssemblyAnalyzerEditor.XamlFileType,
				title
				);
			return xamlViewModel;
		}
		#region XAML
		private Stream GetTypeBamlStream(Type type)
		{
			bool hasContentLoaded = type.GetField("_contentLoaded", BindingFlags.Instance | BindingFlags.NonPublic) != null;
			if (!hasContentLoaded) return null;
			MethodInfo method = type.GetMethod("InitializeComponent", BindingFlags.Instance | BindingFlags.Public);
			if (method == null) return null;

			MsilStringDependency bamlPathDependency = MsilDecompiler.GetDependenciesOf(method).OfType<MsilStringDependency>().FirstOrDefault();
			if (bamlPathDependency == null) return null;

			string bamlPath = bamlPathDependency.GetString();
			{
				int slashIndex = bamlPath.IndexOf('/', 1);
				if (slashIndex > 0) bamlPath = bamlPath.Substring(slashIndex + 1);

				if (bamlPath.EndsWith(".xaml")) bamlPath = bamlPath.Remove(bamlPath.Length - 5) + ".baml";
			}
			Stream bamlStream = type.Assembly.GetPackResource(bamlPath);
			return bamlStream;
		}
		private Action<ICodeWriter> GetXamlBuilder(Stream bamlStream, Assembly assembly)
		{
			return writer =>
			{
				Baml2006Reader bamlReader = XamlDocumentBuilder.CreateThemeBamlReader(bamlStream, assembly);
				XamlDocumentBuilder.Build(
					bamlReader,
					writer,
					assembly,
					this.AssemblyListHost.LoadContext
					);
			};
		}

		public bool HasXaml { get { return this.XamlDocument != null; } }
		public FlowDocument XamlDocument { get; private set; }
		#endregion
		private Action<FlowDocument> GetXmlDocBuilder(
			MemberInfo member,
			XmlDocument documentation,
			NavigationContext navigationContext
			)
		{
			return document => this.BuildXmlDocumentation(
				member,
				documentation,
				document,
				navigationContext
				);
		}
		private void BuildXmlDocumentation(
			MemberInfo member,
			XmlDocument documentation,
			FlowDocument document,
			NavigationContext navigationContext
			)
		{
			MemberViewInfo memberViewInfo = MemberViewInfo.GetMemberViewInfo(member);

			Type type = member as Type;

			string heading = memberViewInfo.Title;

			XmlDocumentationBuilder builder = new XmlDocumentationBuilder(
				document,
				member.Module.Assembly,
				this.AssemblyListHost.LoadContext,
				navigationContext.NavigateToCommand
				);
			builder.WriteText(heading, XmlDocumentationTextType.Title);
			builder.NewParagraph();

			XPathDocument xpathDocument = new XPathDocument(new StringReader(documentation.OuterXml));
			var navigator = xpathDocument.CreateNavigator();

			builder.WriteSection(navigator, "/member/summary");
			switch (member.MemberType)
			{
				case MemberTypes.Property:
					builder.WriteSection(navigator, "/member/value", "Property Value");
					break;
				case MemberTypes.Method:
					{
						builder.WriteSection(navigator, "/member/returns", "Return Value");

						XPathNodeIterator paramNodes = navigator.Select("/member/param");
						if (paramNodes.Count > 0)
						{
							builder.NewParagraph();
							builder.WriteText("Parameters", XmlDocumentationTextType.SectionHeading);

							while (paramNodes.MoveNext())
							{
								string paramName = paramNodes.Current.GetAttribute("name", string.Empty);
								if (!string.IsNullOrEmpty(paramName))
								{
									builder.NewParagraph();
									builder.WriteText(paramName, XmlDocumentationTextType.Parameter);
									builder.WriteLineBreak();
									builder.WriteXml(paramNodes.Current.ReadSubtree());
								}
							}
						}
					}
					break;
			}


			XPathNodeIterator exceptionNodes = navigator.Select("/member/exception");
			if (exceptionNodes.Count > 0)
			{
				builder.NewParagraph();
				builder.WriteText("Exceptions", XmlDocumentationTextType.SectionHeading);

				builder.DocumentBuilder.StartTable();
				builder.WriteText("Exception");
				builder.DocumentBuilder.NewCell();
				builder.WriteText("Description");

				while (exceptionNodes.MoveNext())
				{
					builder.DocumentBuilder.NewRow();
					string cref = exceptionNodes.Current.GetAttribute("cref", string.Empty);
					if (!string.IsNullOrEmpty(cref))
						builder.WriteMemberReference(cref);
					builder.DocumentBuilder.NewCell();
					builder.WriteXml(exceptionNodes.Current.ReadSubtree());
				}

				builder.DocumentBuilder.EndTable();
			}

			builder.WriteSection(navigator, "/member/remarks", "Remarks");

			builder.WriteSection(navigator, "/member/example", "Example");

			XPathNodeIterator seeAlsoNodes = navigator.Select("/member/seealso");
			if (seeAlsoNodes.Count > 0)
			{
				builder.NewParagraph();
				builder.WriteText("See Also", XmlDocumentationTextType.SectionHeading);
				builder.NewParagraph();

				while (seeAlsoNodes.MoveNext())
				{
					string cref = seeAlsoNodes.Current.GetAttribute("cref", string.Empty);
					if (!string.IsNullOrEmpty(cref))
					{
						builder.WriteMemberReference(cref);
						builder.WriteLineBreak();
					}
				}
			}
		}

		protected override DetailViewModel GetDetailViewModelFor(object obj, NavigationContext navigationContext)
		{
			DetailViewModel detail = null;

			Assembly assembly = obj as Assembly;
			if (assembly != null)
			{
				detail = this.GetDetailViewModelForAssembly(assembly, navigationContext);
			}
			else
			{
				MemberInfo member = obj as MemberInfo;
				if (member != null)
				{
					detail = this.GetDetailViewModelForMember(member, navigationContext);
				}
				else
				{
					TreeNodeViewModel treeNode = obj as TreeNodeViewModel;
					if (treeNode != null)
					{
						detail = this.GetDetailViewModelFor(treeNode.Target, navigationContext);
					}
					else
					{
						NamespaceScope namespaceScope = obj as NamespaceScope;
						if (namespaceScope != null)
						{
							detail = this.GetDetailViewModelForNamespace(namespaceScope, navigationContext);
						}
						else
						{
							AssemblyResource resource = obj as AssemblyResource;
							if (resource != null)
							{
								detail = this.GetDetailViewModelForResource(resource, navigationContext);
							}
						}
					}
				}
			}

			if (detail == null)
			{
				// Get providers
				var providers = EnvironmentServices.GetExtensions<IDetailViewModelProvider>();
				foreach (var provider in providers)
				{
					try
					{
						detail = provider.Value.Provide(obj, this);
					}
					catch { /* Protect from external code */ }
					if (detail != null) break;
				}
			}
			if (detail != null)
			{
				// Get extensions
				var extenders = EnvironmentServices.GetExtensions<IDetailViewModelExtender>();
				foreach (var extender in extenders)
				{
					try
					{
						extender.Value.Extend(detail, this);
					}
					catch { /* Protect from external code */ }
				}
			}

			return detail;
		}
		protected DetailViewModel GetDetailViewModelForAssembly(Assembly assembly, NavigationContext navigationContext)
		{
			MemberViewInfo assemblyViewInfo = MemberViewInfo.GetMemberViewInfo(assembly);
			TabbedDetailViewModel assemblyDetail = new AssemblyDetailViewModel(
				assembly,
				navigationContext,
				assemblyViewInfo.Heading,
				this
				);

			return assemblyDetail;
		}
		protected DetailViewModel GetDetailViewModelForNamespace(NamespaceScope namespaceScope, NavigationContext navigationContext)
		{
			TabbedDetailViewModel detail = new TabbedDetailViewModel(
				navigationContext,
				namespaceScope,
				UiString.FromString(namespaceScope.Namespace)
				);

			detail.Summary.Details.Add(new SummaryDetailLinkItem(
				UiString.FromString(Messages.AssemblyLabel),
				UiString.FromString(namespaceScope.Assembly.FullName),
				navigationContext.NavigateToCommand,
				namespaceScope.Assembly
				));
			detail.Tabs.Add(new TabPageViewModel(
				UiString.FromString(Messages.TypesTitle),
				new NamespaceTypesViewModel(namespaceScope, this, navigationContext.NavigateToCommand)
				));

			return detail;
		}
		protected DetailViewModel GetDetailViewModelForResource(AssemblyResource resource, NavigationContext navigationContext)
		{
			TabbedDetailViewModel detail = new TabbedDetailViewModel(navigationContext, resource, UiString.FromString(resource.Name));

			int size = resource.DetermineSize();
			detail.Summary.Details.Add(new SummaryDetailItem(
				UiString.FromString(Messages.SizeLabel),
				UiString.FromString(size.ToString("N0"))
				));

			string title = Path.GetFileNameWithoutExtension(resource.Name);
			string extension = Path.GetExtension(resource.Name);

			if (extension.EndsWith(".baml", StringComparison.InvariantCultureIgnoreCase))
			{
				SourceDocumentViewModel xamlViewModel = this.CreateBamlViewModel(
					resource.GetResourceStream(),
					title,
					resource.Assembly,
					navigationContext
					);
				detail.Tabs.Add(new TabPageViewModel(UiString.FromString(Messages.XamlTitle), xamlViewModel));
			}
			else if (
				extension.Equals(".png", StringComparison.InvariantCultureIgnoreCase)
				|| extension.Equals(".jpg", StringComparison.InvariantCultureIgnoreCase)
				|| extension.Equals(".gif", StringComparison.InvariantCultureIgnoreCase)
				|| extension.Equals(".bmp", StringComparison.InvariantCultureIgnoreCase)
				)
			{
				BitmapFrame imageSource = BitmapFrame.Create(resource.GetResourceStream());
				System.Windows.Controls.Image image = new System.Windows.Controls.Image() { Source = imageSource };
				detail.Tabs.Add(new TabPageViewModel(UiString.FromString(Messages.ImageTitle), image));
			}


			return detail;
		}

		private void BuildMethodBaseMsilDocument(MethodBase method, ICodeWriter codeWriter)
		{
			MsilDocumentBuilder docBuilder = new MsilDocumentBuilder(codeWriter, method, null);
			docBuilder.WriteMethodDeclaration(method);
		}

		private void ExportAssemblySourceCode(object arg)
		{
			Assembly assembly = arg as Assembly;
			if (assembly == null) return;

			ISettingsManager settingsManager = EnvironmentServices.DemandService<ISettingsManager>();
			SourceExportSettings exportSettings = settingsManager.GetSettings<SourceExportSettings>();

			AssemblyExporterViewModel exporterViewModel = new AssemblyExporterViewModel(assembly, this.AssemblyListHost.GetAssemblyIndex(assembly), exportSettings, this);
			EnvironmentServices.DocumentManager.ShowDocument(exporterViewModel);
		}

		private void ViewModuleDetails(object arg)
		{
			string fileName = arg as string;
			if (fileName == null) return;

			PeModule module = PeModule.Open(fileName);
			PeModuleViewModel viewModel = new PeModuleViewModel(module)
			{
				//FileName = this.Path
				Title = UiString.FromString(System.IO.Path.GetFileName(fileName))
			};
			EnvironmentServices.DocumentManager.ShowDocument(viewModel);
		}
	}
}
